using System;

namespace Tool.PEMath
{
    //常用数学运算
    public static class PECalc
    {
        /**
         * 开平方（牛顿迭代法）
         */
        public static PEInt Sqrt(PEInt value,int interatorCount = 8)
        {
            if (value == PEInt.zero)
                return 0;
            if (value < PEInt.zero)
            {
                throw new Exception("value can't be a negative number.");
            }

            PEInt result = value;
            long history;
            int count = 0;
            do {
                history = result.ScaledValue;
                result = (result + value / result) >> 1;
                ++count;
            } while(result.ScaledValue != history && count < interatorCount);
            return result;
        }
        
        /**
         * 获取arccos的值，参数value：a向量*b向量/|a|*|b|（范围-1~1）
         */
        public static PEArgs Acos(PEInt value) 
        {
            //类似半兰伯特的映射（-1~1映射到0~1024）
            PEInt rate = value * AcosTable.HalfIndexCount + AcosTable.HalfIndexCount;
            rate = Clamp(rate, PEInt.zero, AcosTable.IndexCount);
            return new PEArgs(AcosTable.table[rate.RawInt], AcosTable.Multipler);
        }
        
                
        /// <summary>
        /// 返回Sin的值
        /// </summary>
        /// <param name="angleValue">角度值</param>
        /// <returns></returns>
        public static PEInt Sin(int angleValue) 
        {
            angleValue = angleValue % 360;
            angleValue = angleValue < 0 ? angleValue + 360 : angleValue;
            return (PEInt)SinTable.table[angleValue] / SinTable.Multipler;
        }
        
        /// <summary>
        /// 返回Cos的值
        /// </summary>
        /// <param name="angleValue">角度值</param>
        /// <returns></returns>
        public static PEInt Cos(int angleValue) 
        {
            angleValue = angleValue % 360;
            angleValue = angleValue < 0 ? angleValue + 360 : angleValue;
            return (PEInt)CosTable.table[angleValue] / CosTable.Multipler;
        }
        
        //判断两个向量是否在多少角度范围内 向量之间角度<halfRangeAngle?
        //a·b=|a|*|b|*cos(a,b) => cos(a,b)=a·b/|a|*|b|=> cos(a,b)平方=(a·b)平方/|a|平方*|b|平方
        public static bool IsInAngle(PEVector3 from ,PEVector3 to,int halfRangeAngle)
        {
            //不开方的算法
            /*var rawCos = PEVector3.Dot(from, to) / (from.magnitude * to.magnitude);
            var rangeCos = Cos(halfRangeAngle);
            return rawCos >= rangeCos;*/
            //开方算法 性能更好 
            var dot = PEVector3.Dot(from, to);
            var rangeCos = Cos(halfRangeAngle);
            if (dot * rangeCos <= 0)//判断正负值 如果二者符号不同
            {
                return dot >= rangeCos ;//dot为正(angle<180) rangeCos为负(range>180) 则说明在范围内 否则不在
            }
            var rawSqrCos = (dot*dot)/(from.sqrMagnitude * to.sqrMagnitude) ;
            var rangeSqrCos = rangeCos * rangeCos;
            if (dot >= 0)//两者都为正
                return rawSqrCos >= rangeSqrCos;
            else
                return rawSqrCos <= rangeSqrCos;
        }

        public static PEInt Acos2RadianValue(PEInt value) 
        {
            //类似半兰伯特的映射（-1~1映射到0~1024）
            PEInt rate = value * AcosTable.HalfIndexCount + AcosTable.HalfIndexCount;
            rate = Clamp(rate, PEInt.zero, AcosTable.IndexCount);
            return AcosTable.table[rate.RawInt];
        }
        
        public static PEInt Clamp(PEInt input, PEInt min, PEInt max) 
        {
            if(input < min) 
                return min;
            if(input > max) 
                return max;
            return input;
        }
        
        public static PEInt Abs(PEInt value)
        {
            if (value >= 0)
                return value;
            return -value;
        }
    }
}